package com.floreantpos.model;

import com.floreantpos.main.Application;
import java.text.DecimalFormat;
import java.text.SimpleDateFormat;
import java.util.ArrayList;
import java.util.Date;
import java.util.HashSet;
import java.util.List;
import java.util.Set;

import com.floreantpos.model.base.BaseTicket;
import java.text.DecimalFormatSymbols;
import java.util.*;

public class Ticket extends BaseTicket {

  private static final long serialVersionUID = 1L;
  public final static int TAKE_OUT = -1;

  /*[CONSTRUCTOR MARKER BEGIN]*/
  public Ticket() {
    super();
  }

  /**
   * Constructor for primary key
   */
  public Ticket(java.lang.Integer id) {
    super(id);
  }

  /*[CONSTRUCTOR MARKER END]*/
  private static SimpleDateFormat dateFormat = new SimpleDateFormat("dd/MM/yyyy HH:mm");
  private DecimalFormat numberFormat = new DecimalFormat("#,##0.00", new DecimalFormatSymbols(new Locale("pt", "BR")));

  private List deletedItems;

  @Override
  public void setCreateDate(Date createDate) {
    super.setCreateDate(createDate);
    super.setActiveDate(createDate);
  }

  public void addCookingInstruction(TicketCookingInstruction instruction) {
    if (getCookingInstructions() == null) {
      setCookingInstructions(new HashSet<TicketCookingInstruction>());
    }

    getCookingInstructions().add(instruction);
  }

  public void removeCookingInstruction(TicketCookingInstruction instruction) {
    if (getCookingInstructions() == null) {
      return;
    }

    getCookingInstructions().remove(instruction);
  }

  @Override
  public List<TicketItem> getTicketItems() {
    List<TicketItem> items = super.getTicketItems();

    if (items == null) {
      items = new ArrayList<TicketItem>();
      super.setTicketItems(items);
    }
    return items;
  }

  @Override
  public Integer getNumberOfGuests() {
    Integer guests = super.getNumberOfGuests();
    if (guests == null || guests.intValue() == 0) {
      return Integer.valueOf(1);
    }
    return guests;
  }

  public Ticket(User owner, Date createTime) {
    setOwner(owner);
    setCreateDate(createTime);
  }

  public String getCreateDateFormatted() {
    return dateFormat.format(getCreateDate());
  }

  public String getTitle() {
    String title = "";
    if (getId() != null) {
      title += "#" + getId();
    }
    title += " Atendente" + ": " + getOwner();
    title += " Criado em" + ": " + getCreateDateFormatted();
    title += " Total" + ": " + numberFormat.format(getTotalAmount());

    return title;
  }

  public int getBeverageCount() {
    List<TicketItem> ticketItems = getTicketItems();
    if (ticketItems == null) {
      return 0;
    }

    int count = 0;
    for (TicketItem ticketItem : ticketItems) {
      if (ticketItem.isBeverage()) {
        count += ticketItem.getItemCount();
      }
    }
    return count;
  }

  private double calculateSubtotalAmount() {
    double subtotal = 0;

    List<TicketItem> ticketItems = getTicketItems();
    if (ticketItems == null) {
      return subtotal;
    }

    for (TicketItem ticketItem : ticketItems) {
      subtotal += ticketItem.calculateSubtotal(true);
    }

    return subtotal;
  }

  public double getTotalWithGratuity() {

    double totalWithGratuity = 0;

    if (getGratuity() != null && getGratuity().getAmount() != 0) {
      totalWithGratuity = getTotalAmount() + getGratuity().getAmount();
    } else {
      totalWithGratuity = getTotalAmount() + getGratuityAmount();
    }

    return totalWithGratuity;

  }

  public double getDueAmountWithGratuity() {

    return getDueAmount() + getGratuityAmount();
  }

  public double getGratuityAmount() {
    double gratuityAmount = 0;

    if (Application.getGratuityRate() == 0 && getGratuity() != null && getGratuity().getAmount() != 0 && getPaidAmount() == 0) {
      gratuityAmount = getGratuity().getAmount();
    } else if (Application.getGratuityRate() != 0 && getPaidAmount() == 0 && getTableNumber() > 0) {
      gratuityAmount = getTotalAmount() * Application.getGratuityRate() / 100;
    }

    return gratuityAmount;
  }

  private double calculateDiscountAmount() {
    double subtotal = getSubtotalAmount();
    double totalDiscount = 0;

    List<TicketItem> ticketItems = getTicketItems();
    if (ticketItems != null) {
      for (TicketItem ticketItem : ticketItems) {
        totalDiscount += ticketItem.calculateDiscount();
      }
    }

    List<TicketCouponAndDiscount> discounts = getCouponAndDiscounts();
    if (discounts != null) {
      for (TicketCouponAndDiscount discount : discounts) {
        totalDiscount += calculateDiscountFromType(discount, subtotal);
      }
    }

    return totalDiscount;
  }

  private double calculateTax(double subtotalAmount, double discountAmount) {
    List<TicketItem> ticketItems = getTicketItems();
    if (ticketItems == null) {
      return 0;
    }

    double tax = 0;
    for (TicketItem ticketItem : ticketItems) {
      tax += ticketItem.calculateTax(true);
    }

    double subtotalAfterDiscount = subtotalAmount - discountAmount;

    tax = (subtotalAfterDiscount * tax) / subtotalAmount;

    return tax;
  }

  public void calculatePrice() {
    double subtotalAmount = calculateSubtotalAmount();
    double discountAmount = calculateDiscountAmount();
    double taxAmount = calculateTax(subtotalAmount, discountAmount);
    double totalAmount = subtotalAmount - discountAmount + taxAmount;

    if (subtotalAmount < 0 || Double.isNaN(subtotalAmount)) {
      subtotalAmount = 0;
    }
    if (discountAmount < 0 || Double.isNaN(discountAmount)) {
      discountAmount = 0;
    }
    if (taxAmount < 0 || Double.isNaN(taxAmount)) {
      taxAmount = 0;
    }
    if (totalAmount < 0 || Double.isNaN(totalAmount)) {
      totalAmount = 0;
    }

    setSubtotalAmount(subtotalAmount);
    setDiscountAmount(discountAmount);

    if (isTaxExempt()) {
      totalAmount = totalAmount - taxAmount;
      taxAmount = 0;
    }
    setTaxAmount(taxAmount);
    setTotalAmount(totalAmount);

    double dueAmount = totalAmount - getPaidAmount();
    setDueAmount(dueAmount);
  }

  public double calculateDiscountFromType(TicketCouponAndDiscount coupon, double subtotal) {
    List<TicketItem> ticketItems = getTicketItems();

    double discount = 0;
    int type = coupon.getType();
    double couponValue = coupon.getValue();

    switch (type) {
      case CouponAndDiscount.FIXED_PER_ORDER:
        discount += couponValue;
        break;

      case CouponAndDiscount.FIXED_PER_CATEGORY:
        HashSet<Integer> categoryIds = new HashSet<Integer>();
        for (TicketItem item : ticketItems) {
          Integer itemId = item.getItemId();
          if (!categoryIds.contains(itemId)) {
            discount += couponValue;
            categoryIds.add(itemId);
          }
        }
        break;

      case CouponAndDiscount.FIXED_PER_ITEM:
        for (TicketItem item : ticketItems) {
          discount += (couponValue * item.getItemCount());
        }
        break;

      case CouponAndDiscount.PERCENTAGE_PER_ORDER:
        discount += ((subtotal * couponValue) / 100.0);
        break;

      case CouponAndDiscount.PERCENTAGE_PER_CATEGORY:
        categoryIds = new HashSet<Integer>();
        for (TicketItem item : ticketItems) {
          Integer itemId = item.getItemId();
          if (!categoryIds.contains(itemId)) {
            discount += ((item.getUnitPrice() * couponValue) / 100.0);
            categoryIds.add(itemId);
          }
        }
        break;

      case CouponAndDiscount.PERCENTAGE_PER_ITEM:
        for (TicketItem item : ticketItems) {
          discount += ((item.calculateSubtotal(false) * couponValue) / 100.0);
        }
        break;

      case CouponAndDiscount.FREE_AMOUNT:
        discount += couponValue;
        break;
    }
    return discount;
  }

  public void addDeletedItems(Object o) {
    if (deletedItems == null) {
      deletedItems = new ArrayList();
    }

    deletedItems.add(o);
  }

  public List getDeletedItems() {
    return deletedItems;
  }

  public void clearDeletedItems() {
    if (deletedItems != null) {
      deletedItems.clear();
    }

    deletedItems = null;
  }
  
  public String getPaymentMethod() {
    String paymentMethod = getTransactionType();
    
    if (getTransactionSubtype().equals("1")) {
      paymentMethod = "Debito";
    } else if (getTransactionSubtype().equals("2")) {
      paymentMethod = "Credito";
    } else if (getTransactionSubtype().equals("3")) {
      paymentMethod = "Beneficio";
    }
    
    return paymentMethod;
  }
  
  public boolean needsKitchenPrint() {
    if (getDeletedItems() != null && getDeletedItems().size() > 0) {
      return true;
    }

    List<TicketItem> ticketItems = getTicketItems();
    for (TicketItem item : ticketItems) {
      if (item.isShouldPrintToKitchen() && !item.isPrintedToKitchen()) {
        return true;
      }

      List<TicketItemModifierGroup> modifierGroups = item.getTicketItemModifierGroups();
      if (modifierGroups != null) {
        for (TicketItemModifierGroup modifierGroup : modifierGroups) {
          List<TicketItemModifier> ticketItemModifiers = modifierGroup.getTicketItemModifiers();
          if (ticketItemModifiers != null) {
            for (TicketItemModifier modifier : ticketItemModifiers) {
              if (modifier.isShouldPrintToKitchen() && !modifier.isPrintedToKitchen()) {
                return true;
              }
            }
          }
        }
      }
    }

    Set<TicketCookingInstruction> cookingInstructions = getCookingInstructions();
    if (cookingInstructions != null) {
      for (TicketCookingInstruction cookingInstruction : cookingInstructions) {
        if (!cookingInstruction.isPrintedToKitchen()) {
          return true;
        }
      }
    }

    return false;
  }
}
